require([
  'jquery',
  'common/breeze/ammophysicspass',
  'common/breeze/ammothreeutil',
  'common/breeze/callbackpass',
  'common/breeze/gamepadcontroller',
  'common/geometry/geometry3d',
  'common/breeze/inputconstants',
  'common/breeze/keyboardconstants',
  'common/breeze/keyboardcontroller',
  'common/breeze/loop',
  'common/breeze/threerenderpass',
  'lib/ammo',
  'lib/domReady',
  'lib/Three'],
function($, AmmoPhysicsPass, ammothreeutil, CallbackPass, GamepadController,
         geometry3d, inputconstants, keyboardconstants, KeyboardController,
         Loop, ThreeRenderPass, Ammo, domReady, THREE) {

function initScene(scene, camera) {
  camera.position.set(0, 0, 120);
  camera.lookAt(scene.position);

  var light = new THREE.PointLight(0xFFFFFF);
  light.position.set(10, 0, 10);
  scene.add(light);
};

function addPlatform(
  scene, dynamicsWorld, xLength, yLength, zLength, origin, revolutions) {
  var bodyAndMesh = ammothreeutil.createStaticBox(
    xLength, yLength, zLength, origin, geometry3d.unitVectorZ(), revolutions);
  dynamicsWorld.addRigidBody(bodyAndMesh.body);
  scene.add(bodyAndMesh.mesh);
};

function GameObject(
    logicPass, postPhysicsPass, scene, controller, dynamicsWorld) {
  window.gameObject = this;

  this.controller_ = controller;
  this.material_ = new THREE.MeshLambertMaterial( { color: 0xFFFF00 } );

  this.mesh_ = new THREE.Mesh(
    // new THREE.CubeGeometry(5, 5, 5),
    new THREE.CylinderGeometry(
      0,  // Top radius
      2.5,  // Bottom radius
      5,  // Height
      8,  // SegmentsRadius
      1,  // segmentsHeight
      false),  // openEnded
    this.material_);
  this.mesh_.useQuaternion = true;
  this.mesh_.quaternion = new THREE.Quaternion(0, 0, 0, 1);
  scene.add(this.mesh_);

  // var boxShape = new Ammo.btBoxShape(new Ammo.btVector3(2.5, 2.5, 2.5));
  var coneShape = new Ammo.btConeShape(2.5, 5);
  var startTransform = new Ammo.btTransform(new Ammo.btQuaternion(
    this.mesh_.quaternion.x,
    this.mesh_.quaternion.y,
    this.mesh_.quaternion.z,
    this.mesh_.quaternion.w));

  var mass = 1;
  var localInertia = new Ammo.btVector3(0, 0, 0);
  coneShape.calculateLocalInertia(mass, localInertia);
  var myMotionState = new Ammo.btDefaultMotionState(startTransform);
  var rbInfo = new Ammo.btRigidBodyConstructionInfo(
    mass, myMotionState, coneShape, localInertia);

  this.body_ = new Ammo.btRigidBody(rbInfo);
  this.body_.setLinearFactor(new Ammo.btVector3(1, 1, 0));
  this.body_.setAngularFactor(new Ammo.btVector3(0, 0, 0));

  dynamicsWorld.addRigidBody(this.body_);

  this.logicPassKey_ =
    logicPass.addCallback(this.onLogicPass_.bind(this));

  this.postPhysicsPassKey_ =
    postPhysicsPass.addCallback(this.onPostPhysicsPass_.bind(this));
};

GameObject.prototype.onLogicPass_ = function(timeDeltaMillis) {
  var moveVector = this.controller_.getVector(Input.MOVE);

  this.body_.applyCentralForce(new Ammo.btVector3(
    moveVector.x * 100, moveVector.y * 100, 0));

  this.material_.color = new THREE.Color(
    this.controller_.buttonDown(Input.COLOR) ?
      0xFF0000 : 0x0000FF);

  if (this.controller_.lastFrameEventCount(
      Input.JUMP, inputconstants.ButtonEvent.PRESS) > 0) {
    var velocity = this.body_.getLinearVelocity();
    velocity.setY(velocity.y() + 10);
    this.body_.setLinearVelocity(velocity);
  }
};

GameObject.prototype.onPostPhysicsPass_ = function(timeDeltaMillis) {
  var transform = new Ammo.btTransform();
  this.body_.getMotionState().getWorldTransform(transform);

  var origin = transform.getOrigin();
  this.mesh_.position.x = origin.x();
  this.mesh_.position.y = origin.y();
  this.mesh_.position.z = origin.z();

  var rotation = transform.getRotation();
  this.mesh_.quaternion.x = rotation.x();
  this.mesh_.quaternion.y = rotation.y();
  this.mesh_.quaternion.z = rotation.z();
  this.mesh_.quaternion.w = rotation.w();
};

Input = {
  MOVE: 0,
  JUMP: 1,
  COLOR: 2
};

domReady(function() {
  var loop = new Loop();

  // Add render pass first to ensure that one frame is rendered in the
  // initial state.
  var threeRenderPass = new ThreeRenderPass(400, 300);
  $('body').append(threeRenderPass.domElement);
  initScene(threeRenderPass.scene, threeRenderPass.camera);
  loop.addPass(threeRenderPass);

  var logicPass = new CallbackPass();
  loop.addPass(logicPass);

  var resetInputPass = new CallbackPass();
  loop.addPass(resetInputPass);

  var ammoPhysicsPass = new AmmoPhysicsPass();
  loop.addPass(ammoPhysicsPass);

  window.dynamicsWorld = ammoPhysicsPass.dynamicsWorld;

  var postPhysicsPass = new CallbackPass();
  loop.addPass(postPhysicsPass);

  KeyboardController.init(resetInputPass);

/*
  var controller = new KeyboardController();
  controller.mapVector(
    Input.MOVE,
    keyboardconstants.Keycode.W,
    keyboardconstants.Keycode.D,
    keyboardconstants.Keycode.S,
    keyboardconstants.Keycode.A);
  controller.mapButton(
    Input.JUMP, keyboardconstants.Keycode.SPACE);
  controller.mapButton(
    Input.COLOR, keyboardconstants.Keycode.B);
*/

  var controller = new GamepadController(resetInputPass);
  controller.mapVector(Input.MOVE, 0, 1);
  controller.mapButton(Input.JUMP, 2);
  controller.mapButton(Input.COLOR, 3);


  addPlatform(
    threeRenderPass.scene,
    ammoPhysicsPass.dynamicsWorld,
    10, 1, 1,
    new geometry2d.Vector(10, -30),
    1 / 16);

  addPlatform(
    threeRenderPass.scene,
    ammoPhysicsPass.dynamicsWorld,
    10, 1, 1,
    new geometry2d.Vector(-10, -30),
    0);

  var gameObject = new GameObject(
    logicPass,
    postPhysicsPass,
    threeRenderPass.scene,
    controller,
    ammoPhysicsPass.dynamicsWorld);

  loop.start();
});

});  // require
